/* 
 * Enterprise Library Extensions - DI + ORM.
 * Copyright (C) 2006
 *
 * GNU General Public License
 *
 * This program is free software; you can redistribute 
 * it and/or modify it under the terms of the GNU General Public License 
 * as published by the Free Software Foundation; either version 2 of the 
 * License, or (at your option) any later version.
 * 
 * This program is distributed in the hope that it will be useful, but 
 * WITHOUT ANY WARRANTY; without even the implied warranty of 
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. 
 * See the GNU General Public License for more details.
 */
using System;
using System.Collections;

namespace ELE.EnterpriseLibrary.Naming
{
	/// <summary>
  /// This class represents a reference to an object that is found outside of the naming/directory system. 
  /// <br/><br/>Reference provides a way of recording address information about objects which themselves are not 
  /// directly bound to the naming/directory system. 
  /// <br/><br/>A Reference consists of an ordered list of addresses and class information about the object being 
  /// referenced. Each address in the list identifies a communications endpoint for the same conceptual object. 
  /// The "communications endpoint" is information that indicates how to contact the object. It could be, for example, 
  /// a network address, a location in memory on the local machine, another process on the same machine, etc. The order 
  /// of the addresses in the list may be of significance to object factories that interpret the reference. 
  /// <br/><br/>Multiple addresses may arise for various reasons, such as replication or the object offering interfaces 
  /// over more than one communication mechanism. The addresses are indexed starting with zero. 
  /// <br/><br/>A Reference also contains information to assist in creating an instance of the object to which this 
  /// Reference refers. It contains the class name of that object, and the class name and location of the factory to 
  /// be used to create the object. The class factory location is a space-separated list of URLs representing the class 
  /// path used to load the factory. When the factory class (or any class or resource upon which it depends) needs to be 
  /// loaded, each URL is used (in order) to attempt to load the class. 
  /// <br/><br/>A Reference instance is not synchronized against concurrent access by multiple threads. Threads that 
  /// need to access a single Reference concurrently should synchronize amongst themselves and provide the necessary 
  /// locking. 
	/// </summary>
	[Serializable]
	public class Reference : ICloneable
	{
    /// <summary>
    /// Contains the addresses contained in this Reference. 
    /// </summary>
    protected  ArrayList addrs ;
    /// <summary>
    /// Contains the name of the factory class for creating an instance of the object to which this Reference refers. 
    /// </summary>
    protected  string classFactory; 
    /// <summary>
    /// Contains the name of the assembly of the factory class for creating an instance of the object to which 
    /// this Reference refers. 
    /// </summary>
    protected  string classFactoryAssembly; 
    /// <summary>
    /// Contains the location of the factory class. 
    /// </summary>
    protected  string classFactoryLocation; 
    /// <summary>
    /// Contains the fully-qualified name of the class of the object to which this Reference refers. 
    /// </summary>
    protected  string className; 
    /// <summary>
    /// Contains the fully-qualified name of the assembly of the class of the object to which this Reference refers. 
    /// </summary>
    protected  string classAssemblyName; 
    /// <summary>
    /// Constructs a new reference for an object with class name 'className'.
    /// </summary>
    /// <param name="className">The non-null class name of the object to which this reference refers</param>
    public Reference(string className) : this(className, null, null, null, null)
    {
    }
    /// <summary>
    /// ructs a new reference for an object with class name 'className' in the assembly 'assemblyName'. 
    /// Class factory and class factory location are set to null. 
    /// The newly created reference contains zero addresses.
    /// </summary>
    /// <param name="className">The non-null class name of the object to which this reference refers</param>
    /// <param name="assemblyName">The assembly name of the class of the object to which this reference refers</param>
    public Reference(string className, string assemblyName) : this(className, assemblyName, null, null,null)
    {
    }
    /// <summary>
    /// 
    /// </summary>
    /// <param name="className"></param>
    /// <param name="addr"></param>
    public Reference(string className, RefAddr addr) : this(className, null, addr)
    {
    }
    /// <summary>
    /// 
    /// </summary>
    /// <param name="className"></param>
    /// <param name="assemblyName"></param>
    /// <param name="addr"></param>
    public Reference(string className, string assemblyName, RefAddr addr) : this(className, assemblyName, addr, null, null, null)
    {
    }
    /// <summary>
    /// 
    /// </summary>
    /// <param name="className"></param>
    /// <param name="addr"></param>
    /// <param name="factory"></param>
    /// <param name="factoryLocation"></param>
    public Reference(string className, RefAddr addr, string factory, string factoryLocation) : this(className, null, addr, factory, null, factoryLocation)
    {
    }
    /// <summary>
    /// 
    /// </summary>
    /// <param name="className"></param>
    /// <param name="factory"></param>
    /// <param name="factoryLocation"></param>
    public Reference(string className, string factory, string factoryLocation) : this(className, null, factory, factoryLocation)
    {
    }
    /// <summary>
    /// 
    /// </summary>
    /// <param name="className"></param>
    /// <param name="assemblyName"></param>
    /// <param name="factory"></param>
    /// <param name="factoryAssembly"></param>
    /// <param name="factoryLocation"></param>
    public Reference(string className, string assemblyName, string factory,  string factoryAssembly, string factoryLocation) : this(className, assemblyName, null, factory, factoryAssembly, factoryLocation)
    {
    }
    /// <summary>
    /// 
    /// </summary>
    /// <param name="className"></param>
    /// <param name="assemblyName"></param>
    /// <param name="addr"></param>
    /// <param name="factory"></param>
    /// <param name="factoryAssembly"></param>
    /// <param name="factoryLocation"></param>
    public Reference(string className, string assemblyName, RefAddr addr, string factory,  string factoryAssembly, string factoryLocation)
    {
      this.className = className;
      this.classAssemblyName = assemblyName;
      EnsureArray();
      this.addrs.Add(addr);
      this.classFactory = factory;
      this.classFactoryAssembly = factoryAssembly;
      this.classFactoryLocation = factoryLocation;
    }
    /// <summary>
    /// Adds an address to the end of the list of addresses.
    /// </summary>
    /// <param name="addr">The non-null address to add.</param>
    public void Add(RefAddr addr)
    {
      EnsureArray();
      this.addrs.Add(addr);
    }
    /// <summary>
    /// Adds an address to the list of addresses at index posn. 
    /// All addresses at index posn or greater are shifted up the list by one (away from index 0).
    /// </summary>
    /// <param name="posn"></param>
    /// <param name="addr"></param>
    public void Add(int posn, RefAddr addr)
    {

      EnsureArray();
      addrs.Insert(posn,addr);
    }
    /// <summary>
    /// Removes a reference
    /// </summary>
    /// <param name="pos">location in the array</param>
    public void Remove(int pos)
    {
      EnsureArray();
      if(this.addrs.Count > pos && pos > 0)
      {
        this.addrs.RemoveAt(pos);
      }
    }
    /// <summary>
    /// Deletes all addresses from this reference
    /// </summary>
    public void Clear()
    {
      addrs = new ArrayList();
    }
    /// <summary>
    /// Get a reference
    /// </summary>
    /// <param name="posn">location in the array</param>
    /// <returns>The reference</returns>
    public RefAddr Get(int posn)
    {
      EnsureArray();
      return (RefAddr)addrs[posn];
    }
    /// <summary>
    /// Number of addresses
    /// </summary>
    public int Size
    {
      get 
      {
        EnsureArray();
        return addrs.Count;
      }
    }
    /// <summary>
    /// Retrieves the class name of the factory of the object to which this reference refers
    /// </summary>
    public string ClassFactory
    {
      get { return classFactory ; }
    }
    /// <summary>
    /// The assembly name of the factory of the object to which this reference refers
    /// </summary>
    public string ClassFactoryAssembly
    {
      get { return classFactoryAssembly; }
    }
    /// <summary>
    /// The assembly name of the factory of the object to which this reference refers
    /// </summary>
    public string ClassFactoryLocation
    {
      get { return classFactoryLocation; }
    }
    /// <summary>
    /// Retrieves the class name of the object to which this reference refers
    /// </summary>
    public string ClassName
    {
      get { return className; }
    }
    /// <summary>
    /// Get the list of references
    /// </summary>
    public IList All
    {
      get
      {
        EnsureArray();
        return addrs;
      }
    }

    private void EnsureArray()
    {      
      if(addrs == null) addrs = new ArrayList();
    }

    #region ICloneable Members
    /// <summary>
    /// Makes a copy of this reference using its class name list of addresses, 
    /// class factory name and class factory location
    /// </summary>
    /// <returns></returns>
    public object Clone()
    {
      Reference refer = new Reference(this.className, this.classAssemblyName, this.classFactory, this.classFactoryAssembly, this.classFactoryLocation);
      foreach(RefAddr addy in  this.addrs)
      {
        refer.Add(addy);
      }
      return refer;
    }

    #endregion
  }
}
